using Aliyun.OSS;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.IO;
using System.Threading.Tasks;

namespace Infrastructure.AliyunOSS
{
    /// <summary>
    /// 阿里云OSS服务实现
    /// </summary>
    public class AliyunOSSService : IAliyunOSSService, IDisposable
    {
        private readonly OssClient _ossClient;
        private readonly AliyunOSSOptions _options;
        private readonly ILogger<AliyunOSSService> _logger;
        private bool _disposed;

        /// <summary>
        /// 初始化阿里云OSS服务
        /// </summary>
        /// <param name="options">OSS配置选项</param>
        /// <param name="logger">日志记录器</param>
        public AliyunOSSService(IOptions<AliyunOSSOptions> options, ILogger<AliyunOSSService> logger)
        {
            _options = options.Value;
            _logger = logger;

            // 确保Endpoint格式正确
            string endpoint = _options.Endpoint;
            if (!string.IsNullOrEmpty(endpoint))
            {
                // 如果Endpoint不包含协议前缀，则添加https://
                if (!endpoint.StartsWith("http://") && !endpoint.StartsWith("https://"))
                {
                    endpoint = _options.UseHttps ? $"https://{endpoint}" : $"http://{endpoint}";
                }
            }

            // 创建OssClient实例
            _ossClient = new OssClient(
                endpoint,
                _options.AccessKeyId,
                _options.AccessKeySecret);
        }

        /// <inheritdoc />
        public async Task<AliyunOSSResult> UploadAsync(string objectName, Stream fileStream, string contentType)
        {
            return await UploadAsync(_options.BucketName, objectName, fileStream, contentType);
        }

        /// <summary>
        /// 上传文件到指定存储桶
        /// </summary>
        /// <param name="bucketName">存储桶名称</param>
        /// <param name="objectName">对象名称（OSS中的文件路径）</param>
        /// <param name="fileStream">文件流</param>
        /// <param name="contentType">内容类型（如：image/jpeg）</param>
        /// <returns>上传结果</returns>
        public async Task<AliyunOSSResult> UploadAsync(string bucketName, string objectName, Stream fileStream, string contentType)
        {
            try
            {
                // 创建上传请求
                var metadata = new ObjectMetadata
                {
                    ContentType = contentType
                };
                // 异步上传文件
                await Task.Run(() => _ossClient.PutObject(bucketName, objectName, fileStream, metadata));
                // 获取文件URL
                string url = GetObjectUrl(bucketName, objectName);
                _logger.LogInformation("文件上传成功：{BucketName}/{ObjectName}", bucketName, objectName);
                return new AliyunOSSResult
                {
                    Success = true,
                    Url = url,
                    ObjectName = objectName,
                    BucketName = bucketName
                };
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "文件上传失败：{BucketName}/{ObjectName}", bucketName, objectName);
                return new AliyunOSSResult
                {
                    Success = false,
                    ErrorMessage = ex.Message,
                    ObjectName = objectName,
                    BucketName = bucketName
                };
            }
        }

        /// <inheritdoc />
        public async Task<AliyunOSSResult> UploadAsync(string objectName, string filePath)
        {
            return await UploadAsync(_options.BucketName, objectName, filePath);
        }

        /// <summary>
        /// 上传文件到指定存储桶
        /// </summary>
        /// <param name="bucketName">存储桶名称</param>
        /// <param name="objectName">对象名称（OSS中的文件路径）</param>
        /// <param name="filePath">本地文件路径</param>
        /// <returns>上传结果</returns>
        public async Task<AliyunOSSResult> UploadAsync(string bucketName, string objectName, string filePath)
        {
            try
            {
                if (!File.Exists(filePath))
                {
                    _logger.LogError("文件不存在：{FilePath}", filePath);
                    return new AliyunOSSResult
                    {
                        Success = false,
                        ErrorMessage = $"文件不存在：{filePath}",
                        ObjectName = objectName,
                        BucketName = bucketName
                    };
                }

                // 获取文件内容类型
                string contentType = GetContentType(filePath);

                // 异步上传文件
                await Task.Run(() => _ossClient.PutObject(bucketName, objectName, filePath));

                // 获取文件URL
                string url = GetObjectUrl(bucketName, objectName);

                _logger.LogInformation("文件上传成功：{BucketName}/{ObjectName}", bucketName, objectName);

                return new AliyunOSSResult
                {
                    Success = true,
                    Url = url,
                    ObjectName = objectName,
                    BucketName = bucketName
                };
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "文件上传失败：{BucketName}/{ObjectName}", bucketName, objectName);
                return new AliyunOSSResult
                {
                    Success = false,
                    ErrorMessage = ex.Message,
                    ObjectName = objectName,
                    BucketName = bucketName
                };
            }
        }

        /// <inheritdoc />
        public async Task<AliyunOSSResult> UploadAsync(string objectName, byte[] fileBytes, string contentType)
        {
            return await UploadAsync(_options.BucketName, objectName, fileBytes, contentType);
        }

        /// <summary>
        /// 上传文件到指定存储桶
        /// </summary>
        /// <param name="bucketName">存储桶名称</param>
        /// <param name="objectName">对象名称（OSS中的文件路径）</param>
        /// <param name="fileBytes">文件字节数组</param>
        /// <param name="contentType">内容类型（如：image/jpeg）</param>
        /// <returns>上传结果</returns>
        public async Task<AliyunOSSResult> UploadAsync(string bucketName, string objectName, byte[] fileBytes, string contentType)
        {
            try
            {
                using var stream = new MemoryStream(fileBytes);
                
                // 创建上传请求
                var metadata = new ObjectMetadata
                {
                    ContentType = contentType
                };

                // 异步上传文件
                await Task.Run(() => _ossClient.PutObject(bucketName, objectName, stream, metadata));

                // 获取文件URL
                string url = GetObjectUrl(bucketName, objectName);

                _logger.LogInformation("文件上传成功：{BucketName}/{ObjectName}", bucketName, objectName);

                return new AliyunOSSResult
                {
                    Success = true,
                    Url = url,
                    ObjectName = objectName,
                    BucketName = bucketName
                };
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "文件上传失败：{BucketName}/{ObjectName}", bucketName, objectName);
                return new AliyunOSSResult
                {
                    Success = false,
                    ErrorMessage = ex.Message,
                    ObjectName = objectName,
                    BucketName = bucketName
                };
            }
        }

        /// <inheritdoc />
        public async Task<bool> DeleteAsync(string objectName)
        {
            return await DeleteAsync(_options.BucketName, objectName);
        }

        /// <summary>
        /// 从指定存储桶删除文件
        /// </summary>
        /// <param name="bucketName">存储桶名称</param>
        /// <param name="objectName">对象名称（OSS中的文件路径）</param>
        /// <returns>删除结果</returns>
        public async Task<bool> DeleteAsync(string bucketName, string objectName)
        {
            try
            { 
                await Task.Run(() => _ossClient.DeleteObject(bucketName, objectName));
                _logger.LogInformation("文件删除成功：{BucketName}/{ObjectName}", bucketName, objectName);
                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "文件删除失败：{BucketName}/{ObjectName}", bucketName, objectName);
                return false;
            }
        }

        /// <inheritdoc />
        public string GetObjectUrl(string objectName, TimeSpan? expiry = null)
        {
            return GetObjectUrl(_options.BucketName, objectName, expiry);
        }

        /// <summary>
        /// 获取指定存储桶中文件的URL
        /// </summary>
        /// <param name="bucketName">存储桶名称</param>
        /// <param name="objectName">对象名称（OSS中的文件路径）</param>
        /// <param name="expiry">过期时间（默认1小时）</param>
        /// <returns>访问URL</returns>
        public string GetObjectUrl(string bucketName, string objectName, TimeSpan? expiry = null)
        {
            // 如果配置了自定义域名，则使用自定义域名
            if (!string.IsNullOrEmpty(_options.Domain))
            {
                string protocol = _options.UseHttps ? "https://" : "http://";
                return $"{protocol}{_options.Domain}/{objectName}";
            }

            // 否则生成签名URL
            try
            {
                var expiration = DateTime.Now.Add(expiry ?? TimeSpan.FromHours(1));
                var uri = _ossClient.GeneratePresignedUri(bucketName, objectName, expiration);
                
                // 返回干净的URL字符串
                return uri.ToString();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "生成文件URL失败：{BucketName}/{ObjectName}", bucketName, objectName);
                return string.Empty;
            }
        }

        /// <inheritdoc />
        public async Task<bool> ExistsAsync(string objectName)
        {
            return await ExistsAsync(_options.BucketName, objectName);
        }

        /// <summary>
        /// 检查指定存储桶中文件是否存在
        /// </summary>
        /// <param name="bucketName">存储桶名称</param>
        /// <param name="objectName">对象名称（OSS中的文件路径）</param>
        /// <returns>是否存在</returns>
        public async Task<bool> ExistsAsync(string bucketName, string objectName)
        {
            try
            {
                var exist = await Task.Run(() => _ossClient.DoesObjectExist(bucketName, objectName));
                return exist;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "检查文件是否存在失败：{BucketName}/{ObjectName}", bucketName, objectName);
                return false;
            }
        }

        /// <inheritdoc />
        public async Task<byte[]?> DownloadAsync(string objectName)
        {
            return await DownloadAsync(_options.BucketName, objectName);
        }

        /// <summary>
        /// 从指定存储桶下载文件
        /// </summary>
        /// <param name="bucketName">存储桶名称</param>
        /// <param name="objectName">对象名称（OSS中的文件路径）</param>
        /// <returns>文件字节数组</returns>
        public async Task<byte[]?> DownloadAsync(string bucketName, string objectName)
        {
            try
            {
                var result = await Task.Run(() => _ossClient.GetObject(bucketName, objectName));
                
                using var memoryStream = new MemoryStream();
                result.Content.CopyTo(memoryStream);
                
                _logger.LogInformation("文件下载成功：{BucketName}/{ObjectName}", bucketName, objectName);
                
                return memoryStream.ToArray();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "文件下载失败：{BucketName}/{ObjectName}", bucketName, objectName);
                return null;
            }
        }

        /// <summary>
        /// 根据文件扩展名获取内容类型
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <returns>内容类型</returns>
        private string GetContentType(string filePath)
        {
            var extension = Path.GetExtension(filePath).ToLowerInvariant();
            
            return extension switch
            {
                ".jpg" or ".jpeg" => "image/jpeg",
                ".png" => "image/png",
                ".gif" => "image/gif",
                ".bmp" => "image/bmp",
                ".webp" => "image/webp",
                ".pdf" => "application/pdf",
                ".doc" => "application/msword",
                ".docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
                ".xls" => "application/vnd.ms-excel",
                ".xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                ".ppt" => "application/vnd.ms-powerpoint",
                ".pptx" => "application/vnd.openxmlformats-officedocument.presentationml.presentation",
                ".txt" => "text/plain",
                ".html" => "text/html",
                ".css" => "text/css",
                ".js" => "application/javascript",
                ".json" => "application/json",
                ".xml" => "application/xml",
                ".zip" => "application/zip",
                ".rar" => "application/x-rar-compressed",
                ".mp3" => "audio/mpeg",
                ".mp4" => "video/mp4",
                ".avi" => "video/x-msvideo",
                ".mov" => "video/quicktime",
                ".wmv" => "video/x-ms-wmv",
                _ => "application/octet-stream"
            };
        }

        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// 释放资源
        /// </summary>
        /// <param name="disposing">是否正在释放</param>
        protected virtual void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    // 释放托管资源
                }

                _disposed = true;
            }
        }
    }
} 